package com.rengwuxian.coursecoroutines._4_flow

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flattenConcat
import kotlinx.coroutines.flow.flattenMerge
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.zip
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.EmptyCoroutineContext

/**
 * 多个Flow的合并
 * merge()、flattenConcat()/flattenMerge()、
 * flatMapConcat()/flatMapMerge()/flatMapLatest()、
 * combine()/combineTransform()/zip()
 *
 * 1.merge(flow1, flow2):合并两个Flow，就是对两个flow都调用collect，再把他们的数据转发出去，它是完全按照每条数据的发送事件去转发的。
 *                     如果数据是穿插着发送的，那么它就穿插着转发。
 * 2.调用list的merge()方法，效果如上。merge是Iterable的
 *   val flowList = listOf(flow1, flow2)
 *   val mergedFlowFromList = flowList.merge()
 *
 * 3.flow的flow，flatten
 *   val flowFlow = flowOf(flow1, flow2)
 *   flowFlow.flattenConcat() ： 按顺序展开，当第一个flow1被收集和转发完成后，再进行第二个的collect和转发。
 *   flowFlow.flattenMerge() ：合并展开，第一个flow1被收集和转发不会阻碍后面的flow的flow被收集和转发，效果如同穿插式收集和转发
 *
 * 4.把普通的flow转换成flow的flow：
 *   val mappedFlow = flow1.map { from -> (1..from).asFlow().map { "$from - $it" }  }
 *   再铺平展开：mappedFlow.flattenConcat()
 *   效果等同于：
 *   val concattedMappedFlow = flow1.flatMapConcat { from -> (1..from).asFlow().map { "$from - $it" } }
 *   还有一个：flatMapMerge(): 具有穿插式收集和转发的效果。
 *   val mergedMappedFlow = flow1.flatMapMerge { from -> (1..from).asFlow().map { "$from - $it" } }
 *
 * 5.flatMapLatest()：它是一种特殊的先转换成Flow的Flow再顺序展开的操作符。如果第一个flow还没有处理完，第二个flow就开始了，那么就停止对第一个flow的收集和转发。
 *   关键在于Latest()
 *
 * 6.flow1.combine(flow2): 合并两个flow，同时收集两个flow最新的数据，结合出来，生产给下游。
 *   combine(flow1,flow2...): 和上面形式不一样，但是可合并多个
 *
 * 7.flow1.zip(flow2): 合并两个flow，用过的元素就不会再用了，永远是成对的最新的生产的成对数据。
 *
 * 8.flow1.combineTransform(): 需要自己定制发送数据的逻辑。
 *   flow1.combineTransform(flow2) { a, b -> emit("$a - $b") }
 *   combine 和 combineTransform 的关系 就类似于map和mapTransform的关系。
 */
@OptIn(ExperimentalCoroutinesApi::class)
fun main() = runBlocking<Unit> {
  val scope = CoroutineScope(EmptyCoroutineContext)
  val flow1 = flow {
    delay(500)
    emit(1)
    delay(500)
    emit(2)
    delay(500)
    emit(3)
  }
  val flow2 = flow {
    delay(250)
    emit(4)
    delay(500)
    emit(5)
    delay(500)
    emit(6)
  }
  val mergedFlow = merge(flow1, flow2)
  val flowList = listOf(flow1, flow2)
  val mergedFlowFromList = flowList.merge()
  val flowFlow = flowOf(flow1, flow2) // flatten
  val concattedFlowFlow = flowFlow.flattenConcat() // concatenate
  val mergedFlowFlow = flowFlow.flattenMerge()
  val concattedMappedFlow = flow1.flatMapConcat { from -> (1..from).asFlow().map { "$from - $it" } }
  val mergedMappedFlow = flow1.flatMapMerge { from -> (1..from).asFlow().map { "$from - $it" } }
  val latestMappedFlow = flow1.flatMapLatest { from -> (1..from).asFlow().map { "$from - $it" } }
  val combinedFlow = flow1.combine(flow2) { a, b -> "$a - $b" }
  val combinedFlow2 = combine(flow1, flow2, flow1) { a, b, c -> "$a - $b - $c" }
  flow1.combineTransform(flow2) { a, b -> emit("$a - $b") }
  val zippedFlow = flow1.zip(flow2) { a, b -> "$a - $b" }
  scope.launch {
    zippedFlow.collect { println(it) }
  }
  delay(10000)
}